
/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.fhc.todoapp.start2

import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.fhc.todoapp.R
import com.fhc.todoapp.TodoApplication
import com.fhc.todoapp.data.Task
import com.fhc.todoapp.data.TaskRepository
import com.fhc.todoapp.start.StartViewModel.PageAsync
import com.fhc.todoapp.util.UiInitAsync
import com.fhc.todoapp.util.UiStateTask
import com.fhc.todoapp.util.WhileUiSubscribed
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import no.nordicsemi.android.kotlin.ble.scanner.aggregator.BleScanResultAggregator
import no.nordicsemi.android.kotlin.ble.scanner.errors.ScanFailedError
import no.nordicsemi.android.kotlin.ble.scanner.errors.ScanningFailedException
import timber.log.Timber
import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResults
 

data class AlertState(
    var showDialog: Boolean = false,
    var title: String = "",
    var text: String = "",
    var confirmButton: String= "",
    var dismissButton: String= "",
)

enum class LoadingStatus{
    Loading, Error, Success
}

data class LoadingState(
    val showDialog: Boolean = false,
    val status: LoadingStatus = LoadingStatus.Loading,
    val info: String= "",
    val progress: Int = -1,
)

// Represents different states for the LatestNews screen
sealed class PageItemsState {
    data class Success(val news: List<String>): PageItemsState()
    data class Error(val exception: Throwable): PageItemsState()
}


/**
 * ViewModel for the Start screen.
 */
@HiltViewModel
class StartViewModel @Inject constructor(
    private val scannerRepository: ScannerRepository,
    private val taskRepository: TaskRepository
) : ViewModel() {
    private val _toastMsg: MutableStateFlow<Int?> = MutableStateFlow(null)
    private val _isInInit: MutableStateFlow<Boolean?> = MutableStateFlow(null)

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   UI Init  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//    private val _uiInitAsync = flow {
//        //这里加入初始化相关逻辑
//        delay(1000)
//        Timber.tag("_uiInitAsync").e("11111")
//        Timber.tag(">>>>>>>>>>isInInit").e("_uiInitAsync 11111 isInInit  %s", true)
//        emit(UiStateTask(true, R.string.loading_task_error, null))
//
//        delay(6000)
//        Timber.tag("_uiInitAsync").e("22222")
//        Timber.tag(">>>>>>>>>>isInInit").e("_uiInitAsync 22222 isInInit  %s", false)
//        emit(UiStateTask(false, R.string.task_not_found, "AAAAAAAAAAAAA"))
//    }.map { handleUiState(it) }.catch { emit(UiInitAsync.Error(R.string.menu_clear)) }
//    private val usersFlow =
//        MutableSharedFlow<List<User>>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
//    val uiState: StateFlow<UiStateTask> = combine(_toastMsg,  _uiInitAsync) { toastMsg,  uiInitAsync ->
//        when (uiInitAsync) {
//            UiInitAsync.Init -> {
//                Timber.tag(">>>>>>>>>>isInInit").e("UiStateTask Init isInInit  %s", true)
//                UiStateTask(true, null, null)
//            }
//            is UiInitAsync.Error -> {
//                Timber.tag("uiState   Error").e(" ----------------------------------")
//                UiStateTask(false, uiInitAsync.errorMessage, null)
//            }
//            is UiInitAsync.Success -> {
//                Timber.tag("uiState   Success").e(" ----------------------------------")
//                Timber.tag(">>>>>>>>>>isInInit").e("UiStateTask Success isInInit  %s", uiInitAsync.data!!.isInInit)
//                UiStateTask(uiInitAsync.data!!.isInInit, uiInitAsync.data.errMessage, uiInitAsync.data)
//            }
//        }
//    }.stateIn(
//        scope = viewModelScope,
//        started = WhileUiSubscribed,
//        initialValue = UiStateTask(true, null, null)
//    )

//    val uiState: StateFlow<UiStateTask> = combine(_toastMsg) {toastMsg ->  UiStateTask(false, _toastMsg.value, null} // MutableStateFlow(UiStateTask(true, null, null))

    val uiState: StateFlow<UiStateTask> = combine(_toastMsg,_isInInit) { toastMsg, isInInit ->
        UiStateTask(_isInInit.value == true, _toastMsg.value, null)
    }.stateIn(
        scope = viewModelScope,
        started = WhileUiSubscribed,
        initialValue = UiStateTask(true, null, null)
    )

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   Business  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    sealed class PageAsync<out T> {
        object Loading : PageAsync<Nothing>()

        data class Error(val errorMessage: Int) : PageAsync<Nothing>()

        data class Success<out PageTask>(val data: PageTask) : PageAsync<PageTask>()
    }
    data class PageTask(
        var isLoading: Boolean = true,  //用来区分画面是否初始化
        val errMessage: Int? = null,
        val data: MutableList<String>  =  mutableStateListOf<String>(),
        var pageIndex: Int = 0,

        ) {
//    val isEmpty
//        get() = title.isEmpty() || description.isEmpty()
    }

//    private val _isLoading = MutableStateFlow(false)
    // Backing property to avoid state updates from other classes
    private val _pageItemsState: MutableStateFlow<PageItemsState> = MutableStateFlow(PageItemsState.Success(mutableListOf<String>()))




//    private val _pageTask = MutableStateFlow(PageTask(false))
    val pageTask: StateFlow<PageTask> = MutableStateFlow(PageTask(false)).asStateFlow()

    // The UI collects from this StateFlow to get its state updates
//    val pageItemsState = _pageItemsState

//
//    val pageIndex = mutableIntStateOf(1)
//    val pageLoading = mutableStateOf(false)
//    // 用于序列/数组
//    val pageItemList = mutableStateListOf<String>()
//    private val _pageAsync = flow {
//        Timber.tag("_pageAsync flow").e("11111")
//        Thread.sleep(2000)
//        emit(generateFakeData(pageIndex.intValue))
//        Timber.tag("_pageAsync flow").e("2222222")
//    }.map { handlePageData(it) }.onStart { pageLoading.value = false }

//    val uiState: StateFlow<StatisticsUiState> =
//        taskRepository.getTasksStream()
//            .map { Async.Success(it) }
//            .catch<Async<List<Task>>> { emit(Async.Error(R.string.loading_tasks_error)) }
//            .map { taskAsync -> produceStatisticsUiState(taskAsync) }
//            .stateIn(
//                scope = viewModelScope,
//                started = WhileUiSubscribed,
//                initialValue = StatisticsUiState(isLoading = true)
//            )


//    val pageItemsState: StateFlow<Any> = combine(_toastMsg, _isLoading, _pageAsync) { toastMsg, isLoading, pageAsync ->
//        when (pageAsync) {
//            PageAsync.Loading -> {
//                PageTask(true, null, null)
//            }
//            is PageAsync.Error -> {
//                PageTask(false, pageAsync.errorMessage, null)
//            }
//            is PageAsync.Success<PageTask> -> {
//                PageTask(false, null, pageAsync.data)
//            }
//        }
//    }.stateIn(
//        scope = viewModelScope,
//        started = WhileUiSubscribed,
//        initialValue = PageTask(true, null, null)
//    )



//    fun addPageAsync() =  viewModelScope.launch {
//
//
////        pageLoading.value = true   //_pageAsync   onStart 可以
//        _pageAsync.catch { exception ->
//            Timber.tag("_pageAsync exception").e(exception)
//            _pageItemsState.value = PageItemsState.Error(exception)
//        }.collect { value ->
//
//            _pageItemsState.value = PageItemsState.Success(value)
//            Timber.tag("_pageAsync flow").e("66666666666  $value")
//            pageItemList.addAll(value)
//            pageLoading.value = false
//        }
//    }

    private fun handlePageData(task : List<String>?): MutableList<String> {

        val mutableList = mutableListOf<String>()
        task?.forEach{ item -> mutableList.add(item+"   "+mutableList.size+"/"+task.size) }
        return mutableList
    }

    fun generateFakeData(page: Int): List<String> {
        return List(20) { "Item ${(page - 1) * 20 + it + 1}" }
    }
    fun addPage( ) =  viewModelScope.launch {
        Timber.tag("addPage").e("2222   page:%s   %s   %s", pageTask.value.pageIndex, pageTask.value.data.size, pageTask.value.isLoading)
        pageTask.value.isLoading = true
        delay(2000)
        pageTask.value.pageIndex = pageTask.value.pageIndex + 1
        pageTask.value.data.addAll(generateFakeData(pageTask.value.pageIndex + 1))
        pageTask.value.isLoading = false
//        _pageTask.update {
//            it.copy(
//                isLoading = true,
//                errMessage = null
//            )
//        }
//        delay(2000)
//        var data = _pageTask.value.data
//        var pageIndex = _pageTask.value.pageIndex + 1
//        data.addAll(generateFakeData(_pageTask.value.pageIndex + 1))
//        _pageTask.update {
//            it.copy(
//                isLoading = false,
//                errMessage = null,
//                pageIndex = pageIndex,
//                data = data
//            )
//        }
        TodoApplication.instance().getAppViewModel().setNameAge("aaaaaa", 13)
        _toastMsg.value = R.string.murta_name

    }





    private val _alertState = MutableStateFlow(AlertState())
    val alertState: StateFlow<AlertState> = _alertState.asStateFlow()

    fun showDialog(show: Boolean) =  viewModelScope.launch {
        _alertState.update {
            it.copy(
                showDialog = show
            )
        }
    }

    fun alertTitle(title: String) =  viewModelScope.launch {
        _alertState.update {
            it.copy(
                title = title
            )
        }
    }

     fun loadDialog() {
        _alertState.update {
            it.copy(showDialog = true, title = "0000000000000")
        }
        viewModelScope.launch {
            delay(2000)
            _alertState.update {
                it.copy(showDialog = true, title = "aaaaaaaaaaa")
            }
        }
    }



    fun snackbarMessageShown() {
        _toastMsg.value = null
    }

//    fun refresh() {
//        _isLoading.value = true
//        viewModelScope.launch {
//            _isLoading.value = false
//        }
//    }


    private fun handleUiState(task: UiStateTask?): UiInitAsync<UiStateTask?> {
        if (task == null) {
            return UiInitAsync.Error(R.string.task_not_found)
        }
        return UiInitAsync.Success(task)
    }


    private var filters: List<BleScanFilter> = emptyList()

    // This can't be observed in View Model Scope, as it can exist even when the
    // scanner is not visible. Scanner state stops scanning when it is not observed.
    // .stateIn(viewModelScope, SharingStarted.Lazily, ScanningState.Loading)
    private fun List<BleScanResults>.applyFilters(config: List<BleScanFilterState>) =
        filter { result ->
            config.all {
                it.predicate(it.selected, result)
            }
        }

    fun setFilters(filters: List<BleScanFilter>) {
        this.filters = filters
        this._filterConfig.update {
            filters.map {
                BleScanFilterState(it.title, it.initiallySelected, it.filter)
            }
        }
    }

    fun toggleFilter(index: Int) {
        this._filterConfig.value = mutableListOf<BleScanFilterState>()
            .apply { addAll(_filterConfig.value) }
            .apply {
                this[index] = this[index].copy(selected = !this[index].selected)
            }
    }

    private val _filterConfig = MutableStateFlow<List<BleScanFilterState>>(emptyList())
     val filterConfig = _filterConfig.asStateFlow()

    private var currentJob: Job? = null

    private val _state = MutableStateFlow<BleScanningState>(BleScanningState.Loading)
    val state = _state.asStateFlow()

    private fun relaunchScanning() {
        currentJob?.cancel()
        val aggregator = BleScanResultAggregator()
        currentJob = scannerRepository.getScannerState()
            .map { aggregator.aggregate(it) }
            .filter { it.isNotEmpty() }
            .combine(filterConfig) { result, config  ->
                result.applyFilters(config)
            }
            .onStart { _state.value = BleScanningState.Loading }
            .cancellable()
            .onEach {
                // To prevent lags on the device list only refresh the list when
                // it has changed. This simple implementation just checks if
                // any new device was found, which isn't the best, as devices may change
                // advertising data and this won't be shown until some new device is found.
                val shouldRefresh = when (val list = _state.value) {
                    is BleScanningState.DevicesDiscovered -> list.devices.size != it.size
                    else -> true
                }
                if (shouldRefresh) {
                    _state.value = BleScanningState.DevicesDiscovered(it)
                }
            }
            .catch { e ->
                _state.value = (e as? ScanningFailedException)?.let {
                    BleScanningState.Error(it.errorCode.value)
                } ?: BleScanningState.Error(ScanFailedError.UNKNOWN.value)
            }
            .launchIn(viewModelScope)
    }

}
